/// @author Alberto Franco
/// @date   14-04-2011
/// @file   philosofer.go
/// @brief  Contains program entry point. This example shows the dining 
///         philosofer problem in Go programming language. The program after
///         some time go to deadlock! IT IS NOT THE SOLUTION just the problem.
package philosofer

import ( 
    "fmt" 
    "time"
    "rand"
    "sync"
)

///  This is the main function of the problem
func Philosofer_Main() {    
    /// Problem's data.     
    var philoContainer [5]*Philosofer 
    var forks [5]sync.Mutex 
    /// Support data
    var nextFork int = 1
    names :=  [...]string{ "Socrates", "Plato", "Marx", "Kant", "Hegel" }
    
    fmt.Println("Create the philosofers...")
    
    /// Init all the philosofer and assign them the mutexes
    for i := 0; i < 5; i++ {
        if (nextFork == 5) { nextFork = 0 }
        fmt.Printf("Index: %d, next: %d \n",i, nextFork)
        philoContainer[i] = CreatePhilosofer(names[i], 
                            &(forks[nextFork]), &(forks[i])) 
        nextFork++
    }
    
    /// Start the philosofers except the last
    for i := 0; i < 4; i++ {
        go philoContainer[i].Routine()
    }
    /// Start the last philosofer
    philoContainer[4].Routine()
    /// We are not starting all the philosofers in goroutines else the program
    /// would end before starting the real procedure. The main thread has not
    /// to end until the program has finished (never in this case)
}

/// The type that represent a philosofer. A philosofer has a name that 
/// identifies him and two shared locks to represent the forks.
/// PHILO = think.take(RightFork).take(LeftFork).eat.PHILO
type Philosofer struct {
    name       string      ///< The name of the philosofer
    rightFork  *sync.Mutex ///< Lock for the right fork
    leftFork   *sync.Mutex ///< Lock for the left fork
}

/// Build a new philosofer
/// @param name  Philosofer's name
/// @param right Right fork mutex
/// @param left  Left fork mutex
/// @return The newly created philosofer
func CreatePhilosofer(name string, right *sync.Mutex, left *sync.Mutex) *Philosofer {
    var newPhilosofer *Philosofer = new(Philosofer)
    /// Create the new philosofer
    newPhilosofer.name      = name
    newPhilosofer.rightFork = right
    newPhilosofer.leftFork  = left
    
    return newPhilosofer
}

/// Thinking routine. Think a random time in [0, 1sec]
func (self *Philosofer) think () {
    fmt.Println(self.name + " is thinking...")
    time.Sleep(rand.Int63n(1000000))
}

/// The main routine of the philosofer. Acts as described above
func (self *Philosofer) Routine() {
    for {
        self.think()
        /// Take right fork
        self.rightFork.Lock()
        fmt.Println("Right fork taken by " + self.name)
        
        /// Take left fork
        self.leftFork.Lock();
        fmt.Println(self.name + " is eating...")
        /// Release the forks
        self.rightFork.Unlock()
        self.leftFork.Unlock()
    }
}


